﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Wisdom.Core;

/// <summary>
/// Type扩展
/// </summary>
public static class TypeExtensions
{
    private static readonly Type[] PredefinedTypes = {
            typeof(Object),
            typeof(Boolean),
            typeof(Char),
            typeof(String),
            typeof(SByte),
            typeof(Byte),
            typeof(Int16),
            typeof(UInt16),
            typeof(Int32),
            typeof(UInt32),
            typeof(Int64),
            typeof(UInt64),
            typeof(Single),
            typeof(Double),
            typeof(Decimal),
            typeof(DateTime),
            typeof(TimeSpan),
            typeof(Guid),
            typeof(Math),
            typeof(Convert)
        };
    /// <summary>
    /// 判断类型是否为预定义类型
    /// </summary>
    /// <param name="type">需要判断的类型</param>
    /// <returns>是：是预定义类型 否则为：否</returns>
    public static bool IsPredefinedType(this Type type)
    {
        foreach (Type t in PredefinedTypes)
        {
            if (t == type)
            {
                return true;
            }
        }
        return false;
    }
    /// <summary>
    /// 获得类型的名称
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <returns>返回类型的名称</returns>
    public static string FirstSortableProperty(this Type type)
    {
        PropertyInfo firstSortableProperty = type.GetProperties().FirstOrDefault(property => property.PropertyType.IsPredefinedType());

        if (firstSortableProperty == null)
        {
            throw new NotSupportedException(); //TextResource.CannotFindPropertyToSortBy);
        }

        return firstSortableProperty.Name;
    }
    /// <summary>
    /// 判断类型是否为可为空类型
    /// </summary>
    /// <param name="type">需要判断的类型</param>
    /// <returns>是：可空 ，否则：否</returns>
    public static bool IsNullableType(this Type type)
    {
        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    }
    /// <summary>
    /// 获取一个类型的泛型参数类型，若类型不为可空类型，则返回原类型
    /// </summary>
    /// <param name="type">要扩展的类型</param>
    /// <returns>获取一个类型的泛型参数类型，若类型不为可空类型，则返回原类型</returns>
    public static Type GetNonNullableType(this Type type)
    {
        return IsNullableType(type) ? type.GetGenericArguments()[0] : type;
    }
    /// <summary>
    /// 获得类型名称
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <returns>类型名称</returns>
    public static string GetTypeName(this Type type)
    {
        Type baseType = GetNonNullableType(type);
        string s = baseType.Name;
        if (type != baseType) s += '?';
        return s;
    }
    /// <summary>
    /// 判断一个类型是否为数字类型
    /// </summary>
    /// <param name="type">需要判断的类型</param>
    /// <returns>true:是数字 false:非数字</returns>
    public static bool IsNumericType(this Type type)
    {
        return GetNumericTypeKind(type) != 0;
    }
    /// <summary>
    /// 判断类型是否为单精度SignedIntegral
    /// </summary>
    /// <param name="type">需要判断的类型</param>
    /// <returns>true:是单精度 false:非单精度</returns>
    public static bool IsSignedIntegralType(this Type type)
    {
        return GetNumericTypeKind(type) == 2;
    }
    /// <summary>
    /// 判断类型是否为无符号整型
    /// </summary>
    /// <param name="type">需要判断的类型</param>
    /// <returns>true:是无符号整型 false:非无符号整型</returns>
    public static bool IsUnsignedIntegralType(this Type type)
    {
        return GetNumericTypeKind(type) == 3;
    }
    /// <summary>
    /// 获得类型的数字类型
    /// </summary>
    /// <param name="type">需要判断的类型</param>
    /// <returns>1：(Char,Single,Double,Decimal) 2:(SByte,Int16,Int32,Int64) 3:(Byte,UInt16,UInt32,UInt64) 0:其它</returns>
    public static int GetNumericTypeKind(this Type type)
    {
        if (type == null)
        {
            return 0;
        }

        type = GetNonNullableType(type);

        if (type.IsEnum)
        {
            return 0;
        }

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Char:
            case TypeCode.Single:
            case TypeCode.Double:
            case TypeCode.Decimal:
                return 1;
            case TypeCode.SByte:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
                return 2;
            case TypeCode.Byte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return 3;
            default:
                return 0;
        }
    }
    /// <summary>
    /// 获得类型的属性信息
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <param name="indexerArguments">参数类型</param>
    /// <returns>类型的属性信息</returns>
    public static PropertyInfo GetIndexerPropertyInfo(this Type type, params Type[] indexerArguments)
    {
        return
            (from p in type.GetProperties()
             where AreArgumentsApplicable(indexerArguments, p.GetIndexParameters())
             select p).FirstOrDefault();
    }

    private static bool AreArgumentsApplicable(IEnumerable<Type> arguments, IEnumerable<ParameterInfo> parameters)
    {
        var argumentList = arguments.ToList();
        var parameterList = parameters.ToList();

        if (argumentList.Count != parameterList.Count)
        {
            return false;
        }

        for (int i = 0; i < argumentList.Count; i++)
        {
            if (parameterList[i].ParameterType != argumentList[i])
            {
                return false;
            }
        }

        return true;
    }
    /// <summary>
    /// 判断类型是否为枚举
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <returns>true:枚举 false:非枚举</returns>
    public static bool IsEnumType(this Type type)
    {
        return GetNonNullableType(type).IsEnum;
    }
    /// <summary>
    /// 判断两个类型是否兼容
    /// </summary>
    /// <param name="source">源类型</param>
    /// <param name="target">目标类型</param>
    /// <returns>true:兼容 false:不兼容</returns>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
    public static bool IsCompatibleWith(this Type source, Type target)
    {
        if (source == target) return true;
        if (!target.IsValueType) return target.IsAssignableFrom(source);
        Type st = source.GetNonNullableType();
        Type tt = target.GetNonNullableType();
        if (st != source && tt == target) return false;
        TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st);
        TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt);
        switch (sc)
        {
            case TypeCode.SByte:
                switch (tc)
                {
                    case TypeCode.SByte:
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.Byte:
                switch (tc)
                {
                    case TypeCode.Byte:
                    case TypeCode.Int16:
                    case TypeCode.UInt16:
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.Int16:
                switch (tc)
                {
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.UInt16:
                switch (tc)
                {
                    case TypeCode.UInt16:
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.Int32:
                switch (tc)
                {
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.UInt32:
                switch (tc)
                {
                    case TypeCode.UInt32:
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.Int64:
                switch (tc)
                {
                    case TypeCode.Int64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.UInt64:
                switch (tc)
                {
                    case TypeCode.UInt64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                        return true;
                }
                break;
            case TypeCode.Single:
                switch (tc)
                {
                    case TypeCode.Single:
                    case TypeCode.Double:
                        return true;
                }
                break;
            default:
                if (st == tt) return true;
                break;
        }
        return false;
    }
    /// <summary>
    /// 返回类型的泛型类型
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <param name="genericType">泛型类型</param>
    /// <returns>类型的泛型类型</returns>
    public static Type FindGenericType(this Type type, Type genericType)
    {
        while (type != null && type != typeof(object))
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == genericType) return type;
            if (genericType.IsInterface)
            {
                foreach (Type intfType in type.GetInterfaces())
                {
                    Type found = intfType.FindGenericType(genericType);
                    if (found != null) return found;
                }
            }
            type = type.BaseType;
        }
        return null;
    }
    /// <summary>
    /// 返回类型的完全限定名（去掉点号的命名空间+类名）
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <returns>型的完全限定名（去掉点号的命名空间+类名）</returns>
    public static string GetName(this Type type)
    {
        return type.FullName.Replace(type.Namespace + ".", "");
    }
    /// <summary>
    /// 类型的默认值
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <returns>类型的默认值</returns>
    public static object DefaultValue(this Type type)
    {
        if (type.IsValueType)
            return Activator.CreateInstance(type);
        return null;
    }
    /// <summary>
    /// 根据成员名获得类型的特性信息
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <param name="memberName">成员名称</param>
    /// <returns>类型的特性信息</returns>
    public static MemberInfo FindPropertyOrField(this Type type, string memberName)
    {
        MemberInfo memberInfo = type.FindPropertyOrField(memberName, false) ??
                                type.FindPropertyOrField(memberName, true);

        return memberInfo;
    }
    /// <summary>
    /// 根据成员名获得类型的特性信息
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <param name="memberName">成员名称</param>
    /// <param name="staticAccess">是否静态访问</param>
    /// <returns>类型的特性信息</returns>
    public static MemberInfo FindPropertyOrField(this Type type, string memberName, bool staticAccess)
    {
        BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
            (staticAccess ? BindingFlags.Static : BindingFlags.Instance);
        foreach (Type t in type.SelfAndBaseTypes())
        {
            MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field,
                flags, Type.FilterNameIgnoreCase, memberName);
            if (members.Length != 0) return members[0];
        }
        return null;
    }

    /// <summary>
    /// 根据类型获得该类型继承的所有类和接口
    /// </summary>
    /// <param name="type">需在操作的类型</param>
    /// <returns>该类型继承的所有类和接口</returns>
    public static IEnumerable<Type> SelfAndBaseTypes(this Type type)
    {
        if (type.IsInterface)
        {
            var types = new List<Type>();
            AddInterface(types, type);
            return types;
        }
        return SelfAndBaseClasses(type);
    }
    /// <summary>
    /// 获取类型所有继承的类型
    /// </summary>
    /// <param name="type">需要操作的类型</param>
    /// <returns>类型所有继承的类型枚举类型</returns>
    public static IEnumerable<Type> SelfAndBaseClasses(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
    /// <summary>
    /// 根据类型添加所有该类型的接口
    /// </summary>
    /// <param name="types">添加到的接口类型</param>
    /// <param name="type">操作的类型</param>
    static void AddInterface(List<Type> types, Type type)
    {
        if (!types.Contains(type))
        {
            types.Add(type);
            foreach (Type t in type.GetInterfaces()) AddInterface(types, t);
        }
    }
    /// <summary>
    /// 判断类型是否为DataRow类型
    /// </summary>
    /// <param name="type">要操作的类型</param>
    /// <returns>true:数据行类型 false:非数据行类型</returns>
    public static bool IsDataRow(this Type type)
    {
        return type.IsCompatibleWith(typeof(DataRow)) || type.IsCompatibleWith(typeof(DataRowView));
    }

    /// <summary>
    /// 判断类型是否为动态类型
    /// </summary>
    /// <param name="type">要判断的类型</param>
    /// <returns>true:是,false:否</returns>
    public static bool IsDynamicObject(this Type type)
    {
        return type == typeof(object) || type.IsCompatibleWith(typeof(System.Dynamic.IDynamicMetaObjectProvider));
    }

    /// <summary>
    /// 判断类型是否为DateTime类型
    /// </summary>
    /// <param name="type">要判断的类型</param>
    /// <returns>true:是,false:否</returns>
    public static bool IsDateTime(this Type type)
    {
        return type == typeof(DateTime) || type == typeof(DateTime?);
    }
    /// <summary>
    /// 转化类型为JavaScript类型
    /// </summary>
    /// <param name="type">要转化的类型</param>
    /// <returns>JavaScript类型</returns>
    public static string ToJavaScriptType(this Type type)
    {
        if (type == null)
        {
            return "Object";
        }

        if (type == typeof(char) || type == typeof(char?))
        {
            return "String";
        }

        if (IsNumericType(type))
        {
            return "Number";
        }

        if (type == typeof(DateTime) || type == typeof(DateTime?))
        {
            return "Date";
        }

        if (type == typeof(string))
        {
            return "String";
        }

        if (type == typeof(bool) || type == typeof(bool?))
        {
            return "Boolean";
        }

        if (type.IsEnum)
        {
            return "Enum";
        }

        return "Object";
    }
   

    /// <summary>
    /// 获得一个签名字符串
    /// </summary>
    /// <param name="type">扩展类型.</param>
    /// <returns>签名字符串.</returns>
    public static string GetSignature(this Type type)
    {
        var sb = new StringBuilder();

        if (type.IsGenericType)
        {
            sb.Append(type.GetGenericTypeDefinition().FullName);
            sb.Append("[");
            int i = 0;
            var genericArgs = type.GetGenericArguments();
            foreach (var genericArg in genericArgs)
            {
                sb.Append(genericArg.GetSignature());
                if (i != genericArgs.Length - 1)
                    sb.Append(", ");
                i++;
            }
            sb.Append("]");
        }
        else
        {
            if (!string.IsNullOrEmpty(type.FullName))
                sb.Append(type.FullName);
            else if (!string.IsNullOrEmpty(type.Name))
                sb.Append(type.Name);
            else
                sb.Append(type);

        }
        return sb.ToString();
    }

    /// <summary>
    /// Determines whether the specified type has parameter less constructor..
    /// </summary>
    /// <param name="instance">The instance.</param>
    /// <returns>
    /// <c>true</c> if parameter less constructor exists; otherwise, <c>false</c>.
    /// </returns>
    [DebuggerStepThrough]
    public static bool HasDefaultConstructor(this Type instance)
    {
        Guard.IsNotNull(instance, "instance");

        return instance.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Any(ctor => ctor.GetParameters().Length == 0);
    }

    /// <summary>
    /// Gets the public types of the given assembly.
    /// </summary>
    /// <param name="instance">The instance.</param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static IEnumerable<Type> PublicTypes(this Assembly instance)
    {
        IEnumerable<Type> types = null;

        if (instance != null)
        {
            try
            {
                types = instance.GetTypes().Where(type => (type != null) && type.IsPublic && type.IsVisible).ToList();
            }
            catch (ReflectionTypeLoadException e)
            {
                types = e.Types;
            }
        }

        return types ?? Enumerable.Empty<Type>();
    }

    /// <summary>
    /// Gets the public types of the given assemblies.
    /// </summary>
    /// <param name="instance">The instance.</param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static IEnumerable<Type> PublicTypes(this IEnumerable<Assembly> instance)
    {
        return (instance == null) ?
               Enumerable.Empty<Type>() :
               instance.SelectMany(assembly => assembly.PublicTypes());
    }

    /// <summary>
    /// Gets the concretes types of the given assembly.
    /// </summary>
    /// <param name="instance">The instance.</param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static IEnumerable<Type> ConcreteTypes(this Assembly instance)
    {
        return (instance == null) ?
               Enumerable.Empty<Type>() :
               instance.PublicTypes()
                       .Where(type => (type != null) && type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType).ToList();
    }

    /// <summary>
    /// Gets the concretes types of the given assemblies.
    /// </summary>
    /// <param name="instance">The instance.</param>
    /// <returns></returns>
    [DebuggerStepThrough]
    public static IEnumerable<Type> ConcreteTypes(this IEnumerable<Assembly> instance)
    {
        return (instance == null) ?
               Enumerable.Empty<Type>() :
               instance.SelectMany(assembly => assembly.ConcreteTypes());
    }
}
